// main.cpp
// Test driver for Matrix class template project.
// Author:  Jeff Ward

#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>  // for rand()
#include "Matrix.h"

using namespace std;
using namespace nkumath;

template <typename T, size_t ROWS, size_t COLS>
void randomize(Matrix<T, ROWS, COLS> & mat)
// Put random values in a Matrix.
// Note:  It must be possible to assign T an int value.
{
	for (size_t i = 0; i < ROWS; i++)
		for (size_t j = 0; j < COLS; j++)
			mat[i][j] = (rand() % 21) - 10; // Random number in range -10,...,+10
}

struct Complex
{
	Complex(double re = 0.0, double im = 0.0) : real(re), imag(im) { }
	Complex & operator+=(const Complex & rhs)
	{
		real += rhs.real;
		imag += rhs.imag;
		return *this;
	}
	Complex & operator-=(const Complex & rhs)
	{
		real -= rhs.real;
		imag -= rhs.imag;
		return *this;
	}
	Complex & operator*=(const Complex & rhs)
	{
		real = real * rhs.real - imag * rhs.imag;
		imag = real * rhs.imag + imag * rhs.real;
		return *this;
	}
	double real;
	double imag;
};
Complex operator+(const Complex & lhs, const Complex & rhs)
{
	return Complex(lhs.real + rhs.real, lhs.imag + rhs.imag);
}
Complex operator-(const Complex & lhs, const Complex & rhs)
{
	return Complex(lhs.real - rhs.real, lhs.imag - rhs.imag);
}
Complex operator*(const Complex & lhs, const Complex & rhs)
{
	return Complex(lhs.real * rhs.real - lhs.imag * rhs.imag, lhs.real * rhs.imag + lhs.imag * rhs.real);
}
ostream & operator<<(ostream & out, const Complex & c)
{
	out << "(" << c.real << " + " << c.imag << "i)";
	return out;
}


int main()
{
	srand(100);
	ofstream out("output.txt");

	// Matrix construction, operator[], and printing:
	Matrix<int, 4, 5> m1(2);
	out << "m1: " << endl;
	m1.print(out);
	const Matrix<int, 4, 5> m2 = m1;
	out << "m2: " << endl << m2;
	for (size_t i = 0; i < 4; i++)
		m1[i][i] = 5;
	out << "m1: " << endl << m1;

	// Tests of const correctness:
	// m2[0][0] = 0; // This line should not compile.
	// m2 += 4; // Neither should this one.
	int n = m2[0][0]; // This line should be okay.

	// Scalar operation tests:
	out << "m1 += 4: "  << endl << (m1 += 4);
	out << "m1 -= 6: " << endl << (m1 -= 6);
	out << "m1 *= 12: " << endl << (m1 *= 12);
	out << "m1 /= 2: " << endl << (m1 /= 2);
	out << "m1 %= 7: " << endl << (m1 %= 7);

	// Matrix addition and subtraction tests:
	Matrix<int, 4, 5> m3;
	out << "m3: " << endl << m3;
	out << "m3.matrixAdd(m1, m2): " << endl << m3.matrixAdd(m1, m2);
	out << "m3.matrixSubtract(m1, m2): " << endl << m3.matrixSubtract(m1, m2);
	out << "m2 + m1: " << endl << (m2 + m1);
	out << "m2 - m1: " << endl << (m2 - m1);

	// Matrix multiplication tests:
	Matrix<int, 2, 3> m4;
	randomize(m4);
	out << "m4: " << endl << m4;
	Matrix<int, 3, 5> m5;
	randomize(m5);
	out << "m5: " << endl << m5;
	Matrix<int, 2, 5> m6;
	out << "m6.matrixMult(m4, m5): " << endl << m6.matrixMult(m4, m5);
	Matrix<int, 2, 5> m7;
	matrixMult(m4, m5, m7);
	out << "m6 == m7: " << (m6 == m7) << endl;
	out << "m6 == m4 * m5: " << (m6 == m4 * m5) << endl;

	// Matrices of strings:
	Matrix<string, 3, 4> m8("Hello");
	for (size_t i = 0; i < 3; i++)
		m8[i][i] = "   Hi";
	out << "m8: " << endl << m8 << endl;
	Matrix<string, 4, 5> m9(" there!");
	//out << m8 * m9 << endl; // This line should not compile.
	Matrix<string, 3, 4> m10(" there!");
	out << m8 + m10 << endl;

	// Matrices of Complex:
	Matrix<Complex, 2, 8> m11;
	randomize(m11);
	Complex c(1, -3);
	m11 += c;
	out << "m11: " << endl << m11 << endl;
	Matrix<Complex, 8, 3> m12;
	randomize(m12);
	m12 -= c;
	out << "m12: " << endl << m12 << endl;
	out << "m11 * m12: " << endl << m11 * m12 << endl;
	
	out.close();
}